package tianfeng.SecurityDemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author tianfeng
 * @email tianfeng77133@163.com
 * @date 2021/4/5
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    /**
     * 认证授权相关的配置
     * 这个方法用来设置认证中的用户信息，如果用户认证通过，就在auth中设置对应的用户信息
     * 后续的流程通过对auth对象的判断，来定义认证是否通过
     * auth中的部分信息也是作为流程过程中的判定依据
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth
//                .inMemoryAuthentication()  // 内存认证，将认证信息放在缓存中
//                .withUser("lucy")	// 认证的用户名
//                .password(passwordEncoder().encode("123"))	// 认证的用户密码
//                .roles("admin"); // 认证的用户角色
        // 通过 userDetailService 对象进行用户的判断和注入
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 创建一个密码加密对象，用于实现密码的加密
     * spring security5 之后对于密码必须做加密处理
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .logout()                           // 登出操作
                .logoutUrl("/logout")               // 登出路径校验
                .logoutSuccessUrl("/login.html");   // 登出成功跳转路径

        http
                .exceptionHandling()                // 异常处理器
                .accessDeniedPage("/unauth.html");  // 指定 403 页面
        http
                .formLogin()    // 开启表单登录
                .loginPage("/login.html")           // 用户未登录时，访问任何资源都转跳到该路径，即登录页面
                .loginProcessingUrl("/user/login")  // 登录表单form中action的地址，也就是处理认证请求的路径
//                .usernameParameter("uname")         // 登录表单form中用户名输入框input的name名，不修改的话默认是username
//                .passwordParameter("pword")         // form中密码输入框input的name名，不修改的话默认是password
                .defaultSuccessUrl("/user/index", true)   // 登录认证成功后默认转跳的路径
                .and()
                .authorizeRequests()            // 所有的请求
                .antMatchers("/login.html")
                .permitAll()                    // 所有认证用户都可以访问
                .antMatchers("/user/index")
                .hasAnyRole("role")     // 指定角色可以访问
//                .hasAuthority("role")     // 指定权限可以访问
                .anyRequest().authenticated()   // 搭配使用，所有的请求都要授权
                .and().csrf().disable()         // 禁用跨站csrf攻击防御
        ;
        http
                .rememberMe()                           // 开启免登录设置
                .tokenRepository(getPersistentToken())  // 存储token
                .tokenValiditySeconds(60*5);            // 设置token的有效时间
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    // 提供处理token的对象
    @Bean
    public PersistentTokenRepository getPersistentToken() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);      // 接入数据源
        //jdbcTokenRepository.setCreateTableOnStartup(true);  // 是否开启创建表功能
        return jdbcTokenRepository;
    }
}
